;;************************************************************************
;; datavis1.lsp 
;; contains code for visualizing trivariate or multivarite numeric data  
;; copyright (c) 1998 by Forrest W. Young
;;************************************************************************


(defmeth mv-data-object-proto :visualize-n-variables
  (data-matrix variable-labels point-labels &key freq-data)
"Message args: (data-matrix point-labels variable-labels)
Visualization that presents scat-mat, scat-plot, histogram and spin-plot."

  (let* ((variables (column-list data-matrix))
         (nvar (length variables))
         (nobs (send self :nobs))
         (junk (when (> nobs 50) 
                     (send *watcher* :write-text 
                           (format nil "Creating SpreadPlot~%Making Plots"))))
         (spin-margin nil)
         (spin-overlay nil)
         (idle-state nil)
         (cutoff (floor (/ (min screen-size) 85)));controls scatmat scat sizes 
         (nvar-boxplot-initial (min nvar cutoff))
         (scatmat (when (< nvar (1+ cutoff)) 
                        (scatterplot-matrix variables :scale-type nil
                                            :point-labels point-labels
                                            :variable-labels variable-labels
                                            :show nil)))
         (spin-plot (spin-plot  variables :scale-type 'variable
                                :point-labels point-labels
                                :variable-labels variable-labels
                                :show nil))
         (scatter (plot-points variables 
                               :point-labels point-labels
                                :variable-labels variable-labels
                                :show nil))

         (var-list (when (> nvar cutoff)
                         (name-list variable-labels :show nil 
                                    :title "Variables" :menu nil)))
         (q-plot (quantile-plot variables :reg-line t 
                              :variable-labels variable-labels
                              :point-labels point-labels 
                              :show nil))
        ; (np-plot (normal-probability-plot variables :reg-line t 
        ;                      :variable-labels variable-labels
        ;                      :point-labels point-labels 
        ;                      :show nil))
         (box-plot (boxplot (select variables (iseq nvar-boxplot-initial))
                              :variable-labels variable-labels
                              :point-labels point-labels
                              :connect-points t 
                              :equate t
                              :show nil))
         (histofreq (histofreq variables :variable-labels variable-labels
                                        :new-x t :show nil))

         (obs-list (name-list point-labels :title "Observations" :show nil))

         (sp (if var-list
                 (spread-plot (matrix '(2 4)
                    (list var-list spin-plot scatter histofreq 
                          obs-list box-plot nil q-plot ))
                          ;obs-list q-plot box-plot nil
                              :span-right (matrix '(2 4) '(1 1 1 1   1 2 0 1))
                              :rel-widths '(.4 1 1 1))
                 (spread-plot (matrix '(2 4)
                    (list obs-list scatmat spin-plot scatter
                          nil      histofreq q-plot box-plot))
                              :span-down (matrix '(2 4) '(2 1 1 1   0 1 1 1))
                              :rel-widths '(.4 1 1 1))))
         (plot-matrix (send sp :plot-matrix))
         (spin-var (list 0 1 2))
         )
    (send sp :title (strcat "Numeric Data SpreadPlot"))
    (when (> nobs 50) 
          (send *watcher* :write-text 
                (format nil "Creating SpreadPlot~%Making Methods")))
    (send scatter :vista-look-and-feel)

    (defmeth sp :spreadplot-help ()
      (plot-help-window (strcat "SpreadPlot Help"))
      (paste-plot-help (format nil "This is the SpreadPlot for Multivariate Data. In this SpreadPlot the windows are linked by the data's observations and variables.~2%The ~a window, which is in the upper left corner, lets you choose which variables are displayed in other windows. "(if (> nvar cutoff) "Variables" "Scatterplot Matrix")))
(if (> nvar cutoff)
    (paste-plot-help (format nil "You can select a single variable by clicking on a variable name, or you can select several variables by draging or shift-clicking on several names."))
    (paste-plot-help (format nil "You can select a single variable by clicking on a diagonal cell in this matrix, or you can select two variables by clicking on an off-diagonal cell. You can also select several variables by shift-clicking on several cells.")))
(paste-plot-help (format nil "Selecting variables causes new information about the variables to be displayed in the other windows.~2%"))
      (show-plot-help)
      (call-next-method :points t :bars t :labels t :flush nil))

    (send self :add-vncv-spin-features spin-plot nvar nobs cutoff scatter scatmat var-list)
    (send self :add-vncv-qplot-features q-plot)

    (send self :add-vncv-boxplot-features box-plot nobs point-labels)
    (when scatmat  (send self :add-vncv-scatmat-features scatmat nobs ))
    (when var-list 
          (send self :add-vncv-varlist-features var-list obs-list nvar sp))

    (defmeth histofreq :update-plotcell (i j args)
      (when (and (= i 0) (= j 0))
            (send self :new-plot (first (last (first args))))
            ))

    (send obs-list :use-color t)
    (send obs-list :point-color (iseq nobs) 'blue)
    (send obs-list :linked t)
    (send (send obs-list :menu) :title "Obs")
    (send obs-list :fix-name-list)
    
    (send q-plot :redraw)
    (send spin-plot :redraw)
    (send obs-list :redraw)
    (send histofreq :redraw)
    (setf spin-margin (send spin-plot :margin))
    (setf spin-overlay (second (send spin-plot :slot-value 'overlays)))
    
    (when scatmat 
          (send scatmat :redraw))
   ;(send scatter :show-window)
    (send *watcher* :write-text 
                (format nil "Creating SpreadPlot~%Showing Cells"))
    (send sp :show-spreadplot)
    (send *watcher* :hide-window)

    t))


(defmeth mv-data-object-proto :add-vncv-spin-features 
                   (spin-plot nvar nobs cutoff scatter  scatmat var-list)

  (defmeth spin-plot :update-plotcell (i j args)
    (when (and (= i 0) (= j 0))
          (let* ((cur-var-nums (remove-duplicates (first args)))
                 (cur-var-names (remove-duplicates 
                                 (first (second args))  :test 'equal))
                 (numvars (send self :num-variables)))
            (when (= (length cur-var-nums) 2)
                  (apply #'send scatter :content-variables cur-var-nums)
                  (send scatter :start-buffering)
                  (send scatter :clear-lines)
                  (send scatter :adjust-to-data)
                  (send scatter :redraw-curves)
                  (send scatter :buffer-to-screen)
                  (when (send spin-plot :visible)
                        (send spin-plot :visible nil)
                          (send scatter :show-window)
                        (send (if (> nvar cutoff) 
                                  var-list scatmat) 
                              :show-window)))
            (when (or (= (length cur-var-nums) 3)
                      (and (= (length cur-var-nums) 4)
                           (= (third (send self :current-variables)) 
                              (- numvars 1))))
                  (when (= (length cur-var-nums) 4)
                        (setf cur-var-nums (select cur-var-nums '(0 1 2)))
                          (setf cur-var-names (select cur-var-names '(0 1 2)))
                        )
                  (apply #'send self  :current-variables cur-var-nums)
                  (send self :set-variables-with-labels cur-var-nums
                        cur-var-names)
                  (send self :transformation nil :draw nil)
                  (send self :add-box)
                  (when (matrixp (send self :slot-value 'rotation-type))
                        (send self :slot-value 'rotation-type 'yawing))
                    (cond 
                      ((send self :visible)
                       (send self :redraw))
                      (t
                       (send self :visible t)
                       (send self :show-window)
                       (send (if (> nvar cutoff) 
                                 var-list scatmat) 
                             :show-window)))))))

    (send spin-plot :add-slot 'visible)
    (defmeth spin-plot :visible (&optional (logical nil set))
      (if set (setf (slot-value 'visible) logical))
      (slot-value 'visible))

  
    (send spin-plot :scale-type 'centroid-variable)
    (send spin-plot :linked t)
   ; (send spin-plot :depth-cuing nil)
    (send spin-plot :visible t)
    (send spin-plot   :point-symbol
          (iseq (send spin-plot :num-points)) 'disk)
   ; (send spin-plot :use-color t)
    (send spin-plot :point-color (iseq nobs) 'blue)
    (when (< nobs 25) (send spin-plot :showing-labels t))
   ; (send spin-plot :mouse-mode 'hand-rotate)
    (send spin-plot :switch-add-box)
   ; (send spin-plot :set-variables-with-labels '(0 1 2)
   ;       (select (send spin-plot :variable-labels) '(0 1 2))) 
    (send spin-plot :scaled-range 
             (iseq (send spin-plot :num-variables)) -2 2)

   ; (if (> (send spin-plot :num-variables) 3)
   ;     (send spin-plot :plot-buttons :margin nil :box t :new-z t)
   ;     (send spin-plot :plot-buttons :margin nil :box t :new-x nil :new-y nil))
  )
    

(defmeth mv-data-object-proto :add-vncv-qplot-features (qplot)
  (defmeth qplot :update-plotcell (i j args)
    (when (and (= i 0) (= j 0))
          (let ((my-args (first (second args))))
            (send self :show-new-var "Y" (first (last my-args))))))
 ;(print (list "points" (send qplot :points)))
  (send qplot :plot-buttons :mouse-mode (send qplot :points))
  (send qplot :linked (send qplot :points))
  t)

(defmeth mv-data-object-proto :add-vncv-boxplot-features (box-plot nobs point-labels)
  (defmeth box-plot :update-plotcell (i j args)
    (when (and (= i 0) (= j 0));when coming from scatmat
          (let* ((var-nums (remove-duplicates (first args)))
                 (num-vars (length var-nums))
                 (my-args (second args))
                 (var-labs (remove-duplicates (first my-args) :test 'equal))
                 (select-order
                  (combine (sort-and-permute 
                            var-nums (matrix (list num-vars 1) 
                                             (iseq num-vars)))))
                 (var-labs (select var-labs select-order))
                 (data (select (second my-args) select-order)))
            (when (= (length var-labs) 1)
                  (setf data (list (first data))))
            (send self :new-plot data :variable-labels var-labs
                  :point-labels point-labels)))
    (when (and (= i 1) (= j 2));when coming from name-list
          (send self :propagate-selection (first args)))
    (send self :redraw)
    )

  (send box-plot :linked t)
  (when (< nobs 25) (send box-plot :showing-labels t))
  (send box-plot :connect-points t)
  (send box-plot :mouse-mode 'brushing) 
  (send box-plot :use-color t)
  (send box-plot :point-color (iseq (send box-plot :num-points)) 'blue)
  (send box-plot :redraw)
  )

(defmeth mv-data-object-proto :add-vncv-scatmat-features (scatmat nobs)
  (send scatmat :linked t)
  (send scatmat :use-color t)
  (send scatmat :point-color (iseq nobs) 'blue)
  (send scatmat :add-mouse-mode 'focus-on-variables
        :title "Focus On Variables"
        :click :do-new-variable-focus
        :cursor 'finger)
  (send scatmat :plot-buttons :new-x nil :new-y nil)
  (send scatmat :mouse-mode 'focus-on-variables))
  
(defmeth mv-data-object-proto :add-vncv-varlist-features 
                           (var-list obs-list nvar sp)

  (defmeth var-list :do-select-click (x y m1 m2)
    (when (not (send self :has-slot 'old-var-list))
          (send self :add-slot 'old-var-list)
          (defmeth self :old-var-list (&optional (avar-list nil set))
            (if set (setf (slot-value 'old-var-list) avar-list))
            (slot-value 'old-var-list)))
    (call-next-method x y m1 m2)
    (let* ((cur-var  (send self :selection))
           (old-var (send self :old-var-list))
           (nvar nil) (variable-labels nil)
           (var-labs nil) (cur-data nil) )
      (when cur-var
            (when (and m1 (send self :old-var-list))
                  (mapcar 
                   #'(lambda (i) 
                       (setf cur-var 
                             (remove (select old-var i) cur-var)))
                   (iseq (length old-var)))
                  (setf cur-var (combine old-var cur-var )) 
                  )
            (send self :old-var-list cur-var)
            (setf nvar (send self :num-points))
            (setf variable-labels 
                  (send self :point-label (iseq nvar)))
            (setf var-labs (select variable-labels cur-var))
            (setf cur-data 
                  (map-elements #'send current-data 
                                :variable var-labs))
            (send sp :update-spreadplot 0 0  cur-var 
                  (list var-labs cur-data)))))
  (send (send var-list :menu) :title "Vars")
  (send var-list :fix-name-list)
  (send var-list :use-color t)
  (send var-list :point-color (iseq nvar) 'red)
  (send obs-list :linked t)
  (send var-list :redraw))
   